package gogen

import (
	"bytes"
	"fmt"
	"github.com/zeromicro/go-zero/tools/goctl/api/parser/g4/ast"
	"io"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/zeromicro/go-zero/core/collection"
	"github.com/zeromicro/go-zero/tools/goctl/api/spec"
	"github.com/zeromicro/go-zero/tools/goctl/api/util"
	"github.com/zeromicro/go-zero/tools/goctl/pkg/golang"
	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
)

type fileGenConfig struct {
	dir             string
	subdir          string
	filename        string
	templateName    string
	category        string
	templateFile    string
	builtinTemplate string
	data            any
}

func genFile(c fileGenConfig) error {
	fp, created, err := util.MaybeCreateFile(c.dir, c.subdir, c.filename)
	if err != nil {
		return err
	}
	if !created {
		return nil
	}
	defer fp.Close()

	var text string
	if len(c.category) == 0 || len(c.templateFile) == 0 {
		text = c.builtinTemplate
	} else {
		text, err = pathx.LoadTemplate(c.category, c.templateFile, c.builtinTemplate)
		if err != nil {
			return err
		}
	}

	t := template.Must(template.New(c.templateName).Parse(text))
	buffer := new(bytes.Buffer)
	err = t.Execute(buffer, c.data)
	if err != nil {
		return err
	}

	code := golang.FormatCode(buffer.String())
	_, err = fp.WriteString(code)
	return err
}

// 方法采取逐行读取文件中的内容以匹配符合条件的内容进行处理
//
//	processingInContent 只处理节点中间的数据
func addContentToAFile(file, position string, processingInContent func(s string), addContent func(positionStartIdx, positionEndIdx int, contents []string) []string) (string, error) {
	var content string
	if isFile(file) {
		filename := filepath.Join(file)
		body, err := os.ReadFile(filename)
		if err != nil {
			return "", err
		}
		content = string(body)
	} else {
		content = file
	}

	contents := strings.Split(content, "\n")

	var (
		// 是否查找到文件节点
		positionIsFind bool
		// 节点结束位置
		positionEndIdx int
		// 节点开始位置
		positionStartIdx int
	)

	for i, s := range contents {
		if strings.Contains(s, position) {
			positionIsFind = true
			positionStartIdx = i
			continue
		}

		if !positionIsFind {
			continue
		}

		// 结束默认以方法结束
		if strings.Contains(s, "}") {
			positionEndIdx = i
			break
		}

		processingInContent(s)
	}

	contents = addContent(positionStartIdx, positionEndIdx, contents)

	return strings.Join(contents, "\n"), nil
}

func writeProperty(writer io.Writer, name, tag, comment string, tp spec.Type, indent int) (string, error) {
	util.WriteIndent(writer, indent)
	var pkg string
	tpname := tp.Name()
	fieldName := strings.Split(tpname, "_")
	if p, ok := ast.Trope[fieldName[0]]; ok {
		tpname = fmt.Sprintf("%s.%s", fieldName[0], fieldName[1])
		pkg = p
	}
	var err error
	if len(comment) > 0 {
		comment = strings.TrimPrefix(comment, "//")
		comment = "//" + comment
		_, err = fmt.Fprintf(writer, "%s %s %s %s\n", strings.Title(name), tpname, tag, comment)
	} else {
		_, err = fmt.Fprintf(writer, "%s %s %s\n", strings.Title(name), tpname, tag)
	}

	return pkg, err
}

func getAuths(api *spec.ApiSpec) []string {
	authNames := collection.NewSet()
	for _, g := range api.Service.Groups {
		jwt := g.GetAnnotation("jwt")
		if len(jwt) > 0 {
			authNames.Add(jwt)
		}
	}
	return authNames.KeysStr()
}

func getMiddleware(api *spec.ApiSpec) []string {
	result := collection.NewSet()
	for _, g := range api.Service.Groups {
		middleware := g.GetAnnotation("middleware")
		if len(middleware) > 0 {
			for _, item := range strings.Split(middleware, ",") {
				result.Add(strings.TrimSpace(item))
			}
		}
	}

	return result.KeysStr()
}

func responseGoTypeName(r *spec.LogicMethod, pkg ...string) string {
	if r.ResponseType == nil {
		return ""
	}

	resp := golangExpr(r.ResponseType, pkg...)
	switch r.ResponseType.(type) {
	case spec.DefineStruct:
		if !strings.HasPrefix(resp, "*") {
			return "*" + resp
		}
	}

	return resp
}

func requestGoTypeName(r *spec.LogicMethod, pkg ...string) string {
	if r.RequestType == nil {
		return ""
	}

	return golangExpr(r.RequestType, pkg...)
}

func isFile(v string) bool {
	fileInfo, err := os.Stat(v)
	if err != nil {
		return false
	}

	return fileInfo.Mode().IsRegular()
}

func golangExpr(ty spec.Type, pkg ...string) string {
	switch v := ty.(type) {
	case spec.PrimitiveType:
		return v.RawName
	case spec.DefineStruct:
		if len(pkg) > 1 {
			panic("package cannot be more than 1")
		}

		if len(pkg) == 0 {
			return v.RawName
		}

		return fmt.Sprintf("%s.%s", pkg[0], strings.Title(v.RawName))
	case spec.ArrayType:
		if len(pkg) > 1 {
			panic("package cannot be more than 1")
		}

		if len(pkg) == 0 {
			return v.RawName
		}

		return fmt.Sprintf("[]%s", golangExpr(v.Value, pkg...))
	case spec.MapType:
		if len(pkg) > 1 {
			panic("package cannot be more than 1")
		}

		if len(pkg) == 0 {
			return v.RawName
		}

		return fmt.Sprintf("map[%s]%s", v.Key, golangExpr(v.Value, pkg...))
	case spec.PointerType:
		if len(pkg) > 1 {
			panic("package cannot be more than 1")
		}

		if len(pkg) == 0 {
			return v.RawName
		}

		return fmt.Sprintf("*%s", golangExpr(v.Type, pkg...))
	case spec.InterfaceType:
		return v.RawName
	}

	return ""
}

func getDoc(doc string) string {
	if len(doc) == 0 {
		return ""
	}

	return "// " + strings.Trim(doc, "\"")
}

func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

/*
从理论出发，探究框架设计原理，基于全流程项目实践落地，并构建微服务框架从多维度掌握go在微服务中的设计与开发，完成技术生态栈的闭环。

学习目标：
系统掌握微服务核心技术要点，熟悉主流微服务框架go-zero/grpc及微服务生态中的技术栈，同时掌握微服务项目开发能力并能自主探究自研微服务框架

课程重点：
1. 掌握微服务体系中的核心要点
2. 掌握并探究微服务框架go-zero与grpc
3. 掌握在微服务架构下的开发模式
4. 掌握IM核心关键技术的实现方案
5. 掌握微服务架构下的核心问题的解决方案
6. 从用到探究原理，到个性化自主研发设计微服务框架

微服务、docker、go-zero、im

从浅至深多维度学习，GO微服务通关课程

*/
